#include "Framebuffer.h"

ColorRGBA::ColorRGBA(const fp _r, const fp _g, const fp _b, const fp _a)
{
  r=_r;
  g=_g;
  b=_b;
  a=_a;
};

void ColorRGBA::Set(const ColorRGBA& _c) { memcpy(c, _c.c, sizeof(c)); };

ui8 ColorRGBA::GetColorRGB8(ui32 _c, const fp gamma) const
{
  if (_c > 3) _c=3;
  
  if (c[_c] <= 0.0f) return 0;
  
  fp color = c[_c],
     power = 1.0f / gamma,
     gCorrected = 0;
  
  // clamping [0,1]
  if (color > 1.0f) color = 1.0f;
        
  // gamma-correction
  gCorrected = 256.0f*pow(color, power);
  if (gCorrected > 255.0f) gCorrected = 255.0f;
          
  return (ui8)gCorrected;
}

RGBA::RGBA(const ui32 _w, const ui32 _h)
{
  w = _w;
  h = _h;
  cb = NULL;

  data.clear();
  data.reserve(w*h);
  
  for (ui32 i = 0; i < w*h; i++)
    data.push_back(ColorRGBA());
}

bool RGBA::IsValid() const
{
  return (w > 0 &&
          h > 0 &&
          data.size() == w*h);
}

void RGBA::Clear()
{
  for (ui32 i = 0; i < w*h; i++)
    data[i].Clear();
};

i32 Framebuffer::Create(const ui32 w, const ui32 h)
{
  stack.push_back(RGBA(w,h));
  return (i32)(stack.size() - 1);
};

void Framebuffer::Register(const ui32 slot, const Callback cb)
{
  if (slot < stack.size() && cb != NULL) stack[slot].cb = cb;
};

void Framebuffer::Process(const ui32 slot)
{
  if (slot >= stack.size()) return;
    
  RGBA& p = stack[slot];
  if (p.IsValid() != true ||
      p.cb == NULL) return;
    
  for (ui32 line = 0; line < p.h; line++)
    for (ui32 pixel = 0; pixel < p.w; pixel++)
      p.cb(pixel, line, p.data[line*p.w+pixel]);
};

void Framebuffer::Write(const ui32 slot, string title, const fp gamma) const
{
  if (slot >= stack.size()) return;

  const RGBA& p = stack[slot];
  if (p.IsValid() != true) return;

  title += ".ppm";

	stringstream PPM6;
  ofstream f;

  PPM6 << "P6\n# comment\n" << p.w << " " << p.h << "\n255\n";
	
  f.open(title.c_str(), ios::binary | ios::trunc);

	if (! f) return;

  f.write(PPM6.str().c_str(), PPM6.str().size());
  
  vector<char> data;
  
  for (ui32 i = 0, amount = p.w*p.h; i < amount; i++)
    for (ui32 c = 0; c < 3; c++)
      data.push_back(p.data[i].GetColorRGB8(c, gamma));

  f.write(&data[0], data.size());

  f.close();
};
